<script setup>
import {ref, onMounted, watch} from 'vue';
import {Close} from "@element-plus/icons-vue";
import {formatTime} from "@/utils/libs";
import {httpGet} from "@/utils/http"
import {ElMessage} from "element-plus";

// 音乐播放器组件

// 音频元素的引用
const audio = ref(null)
// 播放状态(true:播放中，false:暂停)
const isPlaying = ref(false)
// 当前播放歌曲索引
const songIndex = ref(0)
// 当前播放时间(秒)
const currentTime = ref(0)
// 歌曲总时长(秒)
const duration = ref(100)
// 进度百分比(0-100)
const progressPercent = ref(0)
// 进度条DOM引用
const progressBarRef = ref(null)
// 歌曲标题
const title = ref('')
// 歌曲风格标签
const tags = ref('')
// 歌曲封面
const cover = ref('')

const props = defineProps({
  // 歌曲列表
  songs: {
    type: Array,
    required: true,
    default: () => []
  },
  // 是否显示关闭按钮
  showClose: {
    type: Boolean,
    default: false
  },
  songIndex: {
    type: Number,
    default: 0
  }
})

const emits = defineEmits(['close', 'play'])

watch(() => props.songIndex, (newVal) => {
  songIndex.value = parseInt(newVal)
  loadSong(props.songs[songIndex.value])
})

watch(() => props.songs, (newVal) => {
  loadSong(newVal[songIndex.value])
})

// 加载歌曲资源
const loadSong = (song) => {
  if (!song) {
    return ElMessage.error('歌曲加载失败')
  }
  title.value = song.title
  tags.value = song.tags
  cover.value = song.cover_url
  // 将音频元素的src属性设置为歌曲的音频URL。
  audio.value.src = song.audio_url
  // 调用load()方法重新加载音频，这将重置音频元素并开始加载新的音频资源。
  audio.value.load()
  // 将播放状态设置为false（暂停状态），因为加载新歌曲后我们不会自动播放
  isPlaying.value = false
  // 当音频的元数据（如时长）加载完成后，将总时长（duration）设置为音频元素的实际时长。
  audio.value.onloadedmetadata = () => {
    duration.value = audio.value.duration
  }
}

// 切换播放/暂停状态
const togglePlay = () => {
  // 检查当前播放状态是否为播放中（true）
  if (isPlaying.value) {
    // 如果是播放中，则执行暂停操作
    audio.value.pause() // 调用音频元素的暂停方法
    isPlaying.value = false // 将播放状态设置为暂停（false）
  } else {
    // 如果不是播放中（即暂停状态），则执行播放操作
    play()
  }
}

// 播放音频
const play = () => {
  if (isPlaying.value) {
    return
  }
  // 调用原生音频元素的play()方法，开始播放音频。
  audio.value.play()
  isPlaying.value = true
  // 检查音频的当前播放时间是否为0（即从头开始播放）。这通常发生在以下情况：
  //    第一次播放这首歌（刚加载完）
  //    手动将进度条拖回开头后播放
  // 注意：如果用户从中间暂停后再继续播放，由于currentTime不为0，所以不会进入这个条件。
  if (audio.value.currentTime === 0) {
    // 触发一个名为'play'的自定义事件，通知父组件播放开始了（从0开始播放）
    emits('play')
    // 增加播放量
    httpGet('api/suno/play', {song_id: props.songs[songIndex.value].song_id}).then().catch()
  }
}

// 播放器切换到上一首歌曲的功能
const prevSong = () => {
  // 计算上一首歌曲的索引：通过 (当前索引 - 1 + 歌曲列表长度) % 歌曲列表长度 来实现循环（当到第一首时跳到最后）
  // songIndex.value - 1：获取上一首歌曲的索引
  // + props.songs.length：确保结果为正值（避免负数）
  // % props.songs.length：取模运算确保索引在0到歌曲列表长度-1之间
  songIndex.value = (songIndex.value - 1 + props.songs.length) % props.songs.length
  loadSong(props.songs[songIndex.value])
  audio.value.play()
  isPlaying.value = true
}

// 切换到播放列表中的下一首歌曲
const nextSong = () => {
  // 将当前索引加1，然后对歌曲列表长度取模
  // 这样会在到达列表末尾时循环回第一首（索引0）
  songIndex.value = (songIndex.value + 1) % props.songs.length
  loadSong(props.songs[songIndex.value])
  audio.value.play()
  isPlaying.value = true
}

// 更新播放进度，它在音频播放过程中被触发
const updateProgress = () => {
  try {
    // 将currentTime.value设置为音频元素当前播放的时间（单位：秒）
    currentTime.value = audio.value.currentTime
    // 计算进度条的百分比
    // 公式：（当前时间 / 总时长）* 100
    progressPercent.value = (currentTime.value / duration.value) * 100
  } catch (e) {
    console.error(e.message)
  }
}

// 响应用户在进度条上的点击事件
const setProgress = (event) => {
  // 1. 获取进度条总宽度
  const totalWidth = progressBarRef.value.offsetWidth
  // 2. 获取点击位置距离进度条左侧的距离
  const clickX = event.offsetX
  // 3. 获取音频总时长
  const audioDuration = audio.value.duration
  // 4. 计算并设置新的播放时间
  audio.value.currentTime = (clickX / totalWidth) * audioDuration
}

// 暴露play方法：
// 将组件内部的play函数暴露出去
// 使得父组件可以通过ref直接调用子组件的play方法
defineExpose({
  play
})

onMounted(() => {
  songIndex.value = props.songIndex
  loadSong(props.songs[songIndex.value])
})

</script>

<template>
  <div class="player">
    <div class="container">
      <!-- 封面展示 -->
      <div class="cover">
        <el-image :src="cover" fit="cover"/>
      </div>
      <!-- 歌曲信息展示区域 -->
      <div class="info">
        <div class="title">{{ title }}</div>
        <div class="style">
          <span class="tags">{{ tags }}</span>
          <span class="text-lightGray"> | </span>
          <span class="time">{{ formatTime(currentTime) }}<span class="split">/</span>{{ formatTime(duration) }}</span>
        </div>
      </div>
      <!-- 控制按钮区域 -->
      <div class="controls-container">
        <div class="controls">
          <button @click="prevSong" class="control-btn"><i class="iconfont icon-prev"></i></button>
          <button @click="togglePlay" class="control-btn">
            <i class="iconfont icon-play" v-if="!isPlaying"></i>
            <i class="iconfont icon-pause" v-else></i>
          </button>
          <button @click="nextSong" class="control-btn"><i class="iconfont icon-next"></i></button>
        </div>
      </div>
      <!-- 进度条 -->
      <div class="progress-bar" @click="setProgress" ref="progressBarRef">
        <div class="progress" :style="{width: `${progressPercent}%`}"></div>
      </div>
      <!-- 音频播放器 -->
      <audio ref="audio" @timeupdate="updateProgress" @ended="nextSong"></audio>
      <!-- 关闭按钮 -->
      <el-button v-if="showClose" class="close" type="info" :icon="Close" circle size="small" @click="emits('close')"/>
    </div>
  </div>
</template>

<style scoped lang="stylus">
.player {
  display flex
  justify-content center
  width 100%

  .container {
    display flex
    background-color: #363030;
    border-radius: 10px;
    border 1px solid #544F4F;
    padding: 5px;
    width: 80%
    text-align: center;
    position relative
    overflow hidden


    .cover {
      .el-image {
        border-radius: 50%;
        width 50px
      }
    }

    .info {
      padding 0 10px
      min-width 300px
      display flex
      justify-content center
      align-items flex-start
      flex-flow column
      line-height 1.5

      .title {
        font-weight 700
        font-size 16px
        color #e5e7eb
      }

      .style {
        font-size 14px
        display flex
        color #e1e1e1

        .tags {
          font-weight 600
          white-space: nowrap; /* 防止文本换行 */
          overflow: hidden; /* 隐藏溢出的文本 */
          text-overflow: ellipsis; /* 使用省略号表示溢出的文本 */
          max-width 200px
        }

        .text-lightGray {
          color: rgb(114 110 108);
          padding 0 3px
        }

        .time {
          font-family 'Input Sans'
          font-weight 700

          .split {
            font-size 12px
            position relative
            top -2px
            margin 0 1px 0 3px
          }
        }
      }
    }

    .controls-container {
      width 100%
      display flex
      flex-flow column
      justify-content center

      .controls {
        display: flex;
        justify-content: space-around;
        margin-bottom 10px

        .control-btn {
          background: none;
          border: none;
          color: #fff;
          cursor: pointer;
          background-color #363030
          border-radius 5px
          padding 6px

          .iconfont {
            font-size 20px
          }

          &:hover {
            background-color #5F5958
          }
        }

      }
    }

    .progress-bar {
      position absolute
      width 100%
      left 0
      bottom 0
      height: 8px;
      background-color: #555;
      cursor: pointer;

      .progress {
        height: 100%;
        background-color: #f50;
        border-radius: 5px;
        width: 0;
      }

    }

    .close {
      position absolute
      right 10px
      top 15px
    }
  }
}
</style>